﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenTK.Graphics.OpenGL;
using Tao.OpenGl;
using OpenTK;

namespace opentk_first_project
{
    public enum Tranform { MOVE_LEFT, MOVE_RIGHT, MOVE_FORWARD, MOVE_BACKWARD , ELEVATE, ROLL, PITCH, YAW }
    class Camera
    {
        float[] mat = new float[16];
        Vector3 forward;
        Vector3 right;
        Vector3 up;
        Vector3 eyePos = new Vector3(100,100,100);
        Vector3 vR = new Vector3(-100, -0, -100);
        Vector3 upDir = new Vector3(0, 1, 0);
        public float zNear = 0;
        public float zFar = 0;
        //Field for movement
        Resolution resolution;
        float fovy = 90; // field of view FoV in y direction

        public Camera(int width, int height)
        {
            resolution = new Resolution((int)width, (int)height);
            zNear = 0; // always positive
            zFar = 2000; // always positive
        }

        public void UpdateSize(int width, int height)
        {
            resolution.Width = width;
            resolution.Height = height;
            Reshape();
        }
       

        public void Reshape()            
        {
            if (resolution.Width == 0 || resolution.Height == 0)
            {
                //minimize window
                return;
            }
            double halfWidth = resolution.Width / 2;
            double halfHeight = resolution.Height / 2;
            Gl.glViewport(0, 0, resolution.Width, resolution.Height);
            Gl.glMatrixMode(Gl.GL_PROJECTION);
            Gl.glLoadIdentity();
            Glu.gluPerspective(fovy, resolution.Width / resolution.Height, zNear, zFar);
            Gl.glMatrixMode(Gl.GL_MODELVIEW);
            Gl.glLoadIdentity();
            Glu.gluLookAt(eyePos.X, eyePos.Y, eyePos.Z, eyePos.X + vR.X, eyePos.Y + vR.Y, eyePos.Z + vR.Z,
                upDir.X, upDir.Y, upDir.Z);

        }
        public void Active(Tranform tranform, float value)
        {
            GL.LoadIdentity();
            Glu.gluLookAt(eyePos.X, eyePos.Y, eyePos.Z, eyePos.X + vR.X, eyePos.Y + vR.Y, eyePos.Z + vR.Z,
                upDir.X, upDir.Y, upDir.Z);
            // get model view matrix
            UpdateModelViewMatrix();
            switch (tranform)
            {
                case Tranform.MOVE_FORWARD:
                    forward *= -1;
                    Move(forward, value);
                    break;
                case Tranform.MOVE_BACKWARD:
                    Move(forward, value);
                    break;
                case Tranform.MOVE_LEFT:
                    right *= -1;
                    Move(right, value);
                    break;
                case Tranform.MOVE_RIGHT:
                    Move(right, value);
                    break;
                case Tranform.ELEVATE:
                    Elevate(up, value);
                    break;
                case Tranform.ROLL:
                    Roll(value);
                    break;
                case Tranform.PITCH:
                    Pitch(value);
                    break;
                case Tranform.YAW:
                    Yaw(value);
                    break;
            }
        }

        private void UpdateModelViewMatrix()
        {
            Gl.glGetFloatv(Gl.GL_MODELVIEW_MATRIX, mat);
            right = new Vector3(mat[0], mat[4], mat[8]);
            up = new Vector3(mat[1], mat[5], mat[9]);
            forward = new Vector3(mat[2], mat[6], mat[10]);
        }

        private void Move(Vector3 direction, float speed)
        {
            eyePos += direction*speed;
        }
        private void Elevate(Vector3 direction, float speed)
        {
            eyePos += direction * speed;
        }
        private void Roll(float angle)
        {
            GL.Translate(eyePos.X, eyePos.Y, eyePos.Z);
            GL.Rotate(angle, new Vector3d(forward.X, forward.Y, forward.Z));
            GL.Translate(-eyePos.X, -eyePos.Y, -eyePos.Z);
            UpdateModelViewMatrix();
            upDir = up;
        }
        private void Pitch(float angle)
        {
            GL.Translate(eyePos.X, eyePos.Y, eyePos.Z);
            GL.Rotate(angle, new Vector3d(right.X , right.Y, right.Z));
            GL.Translate(-eyePos.X, -eyePos.Y, -eyePos.Z);
            UpdateModelViewMatrix();
            vR = forward * -1 * vR.Length;
            upDir = up;
            
        }
        private void Yaw(float angle)
        {
            GL.Translate(eyePos.X, eyePos.Y, eyePos.Z);
            GL.Rotate(angle, new Vector3d(up.X, up.Y, up.Z));
            GL.Translate(-eyePos.X, -eyePos.Y, -eyePos.Z);
            UpdateModelViewMatrix();
            vR = forward * -1 * vR.Length;
        }

        public void Zoom(float deltaY)
        {
            deltaY = deltaY / resolution.Height;
            eyePos = eyePos  - (vR) * (deltaY );
            vR = vR + vR * deltaY;
        }

        private Vector3 rotationPoint(Vector3 p, Vector3 v, Vector3 o, double angle)
        {
            Vector3 ret = Vector3.Zero;
            double c = Math.Cos(angle);
            double s = Math.Sin(angle);
            double C = 1 - Math.Cos(angle);
            //Matrix 3x3
            Vector3 row1 = new Vector3((float)(v.X * v.X * C + c), (float)(v.X * v.Y * C - v.Z * s),
                (float)(v.X * v.Z * C + v.Y * s));
            Vector3 row2 = new Vector3((float)(v.Y * v.X * C + v.Z * s), (float)(v.Y * v.Y * C + c),
                (float)(v.Y * v.Z * C - v.X * s));
            Vector3 row3 = new Vector3((float)(v.Z * v.X * C - v.Y * s), (float)(v.Z * v.Y * C + v.X * s),
                (float)(v.Z * v.Z * C + c));
            Vector3 pSubo = p - o;
            float det = row1.X * row2.Y * row3.Z + row1.Y * row2.Z * row3.X + row1.Z * row2.X * row3.Y
                - row1.Z * row2.Y * row3.X - row1.Y * row2.X * row3.Z - row1.X * row2.Z * row3.Y;
            ret = o + pSubo * det;
            
            return ret;
        }
        private Vector3 multiVector3(Vector3 a, Vector3 b)
        {
            Vector3 ret = Vector3.Zero;
            ret.X = a.Y * b.Z - b.Y * a.Z;
            ret.Y = a.Z * b.X - b.Z * a.X;
            ret.Z = a.X * b.Y - b.X * a.Y;            
            return ret;            
        }

        private float dotVector3(Vector3 a, Vector3 b)
        {            
            return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
        }

        private Vector3 cloneVector3(Vector3 a)
        {
            Vector3 ret = Vector3.Zero;
            ret.X = a.X;
            ret.Y = a.Y;
            ret.Z = a.Z;
            return ret;
        }



    }
      
}
